import ctypes
import math
import os

import cv2
import keyboard
import numpy as np
import pyautogui
import pynput
from mss import mss

print('Program Started.')

pyautogui.PAUSE = 0
SendInput = ctypes.windll.user32.SendInput
sct = mss()

net = cv2.dnn.readNetFromDarknet(os.path.dirname(__file__) + '/model/config.cfg',
                                 os.path.dirname(__file__) + '/model/model.weights')
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)

screenWidth = int(input('please input your screen Width (default 2560)') or 2560)
screenHeight = int(input('please input your screen Height (default 1440)') or 1440)
fovSize = int(input('please input FovSize (default 320)') or 320)

opponentTeam = 'ct'
classesNames = ['ct', 'ct_head', 't', 't_head']
monitor = {"top": int(screenHeight / 2 - fovSize / 2), "left": int(screenWidth / 2 - fovSize / 2),
           "width": fovSize, "height": fovSize}


def set_pos(x, y):
    x = 1 + int(x * 65536. / screenWidth)
    y = 1 + int(y * 65536. / screenHeight)
    extra = ctypes.c_ulong(0)
    ii_ = pynput._util.win32.INPUT_union()
    ii_.mi = pynput._util.win32.MOUSEINPUT(x, y, 0, (0x0001 | 0x8000), 0,
                                           ctypes.cast(ctypes.pointer(extra), ctypes.c_void_p))
    command = pynput._util.win32.INPUT(ctypes.c_ulong(0), ii_)
    SendInput(1, ctypes.pointer(command), ctypes.sizeof(command))


def getOpponentPosition(frame, outputs, opponentTeam):
    hT, wT, cT = frame.shape
    bbox = []
    classIds = []
    confs = []
    headBoxes = []
    bodyBoxes = []
    for output in outputs:
        for det in output:
            scores = det[5:]
            classId = np.argmax(scores)
            confidence = scores[classId]
            if confidence > 0.5:
                w, h = int(det[2] * wT), int(det[3] * hT)
                x, y = int((det[0] * wT) - (w / 2)), int((det[1] * hT) - (h / 2))
                bbox.append([x, y, w, h])
                classIds.append(classId)
                confs.append(float(confidence))
    indices = cv2.dnn.NMSBoxes(bbox, confs, 0.5, 0.3)
    for i in indices:
        i = i[0]
        box = bbox[i]
        x, y, w, h = box[0], box[1], box[2], box[3]
        if opponentTeam == 'ct':
            if classIds[i] == 0:
                bodyBoxes.append([x, y, w, h])
            elif classIds[i] == 1:
                headBoxes.append([x, y, w, h])
        if opponentTeam == 't':
            if classIds[i] == 2:
                bodyBoxes.append([x, y, w, h])
            elif classIds[i] == 3:
                headBoxes.append([x, y, w, h])
    if len(headBoxes) > 0 and len(bodyBoxes) > 0:
        return headBoxes, bodyBoxes
    elif len(headBoxes) > 0:
        return headBoxes, None
    elif len(bodyBoxes) > 0:
        return None, bodyBoxes
    else:
        return None, None


def getClosestTarget(mousePoint, headBoxes):
    distance = math.sqrt(((mousePoint[0] - headBoxes[0][0]) ** 2) + ((mousePoint[1] - headBoxes[0][1]) ** 2))
    closestBbox = headBoxes[0]
    if len(headBoxes) > 1:
        for bbox in headBoxes:
            if math.sqrt(((mousePoint[0] - bbox[0]) ** 2) + ((mousePoint[1] - bbox[1]) ** 2)) < distance:
                distance = math.sqrt(((mousePoint[0] - bbox[0]) ** 2) + ((mousePoint[1] - bbox[1]) ** 2))
                closestBbox = bbox
    return closestBbox


print()
print('Aimbot Enabled')
print('Your config ->')
print('\tscreenWidth: ' + str(screenWidth))
print('\tscreenHeight: ' + str(screenHeight))
print('\tFovSize: ' + str(fovSize))
print()
print('press F4 to Exit')
print('press F6 to toggle aim ct or t (default ct)')

while True:
    try:
        if keyboard.is_pressed('f4'):
            break
        if keyboard.is_pressed('f6'):
            if opponentTeam == 'ct':
                opponentTeam = 't'
            else:
                opponentTeam = 'ct'
            print('you change the aim target to -- ' + opponentTeam)
            cv2.waitKey(150)
    except:
        pass

    frame = sct.grab(monitor)

    cur_x, cur_y = pyautogui.position()
    currentPositionPoint = [cur_x, cur_y]

    frame = cv2.cvtColor(np.array(frame), cv2.COLOR_RGBA2RGB)

    blob = cv2.dnn.blobFromImage(frame, 1 / 127, (fovSize, fovSize), [0, 0, 0], 1, crop=False)
    net.setInput(blob)

    layerNames = net.getLayerNames()
    outputNames = [layerNames[i[0] - 1] for i in net.getUnconnectedOutLayers()]
    outputs = net.forward(outputNames)

    headBoxes, bodyBoxes = getOpponentPosition(frame, outputs, opponentTeam)

    if bodyBoxes is not None:
        closestBbox = getClosestTarget(currentPositionPoint, bodyBoxes)
        x, y, w, h = closestBbox[0], closestBbox[1], closestBbox[2], closestBbox[3]
        set_pos(int(x + (w / 2) + (screenWidth / 2 - fovSize / 2)),
                int(y + (h / 2) + (screenHeight / 2 - fovSize / 2)))
    elif headBoxes is not None:
        closestBbox = getClosestTarget(currentPositionPoint, headBoxes)
        x, y, w, h = closestBbox[0], closestBbox[1], closestBbox[2], closestBbox[3]
        set_pos(int(x + (w / 2) + (screenWidth / 2 - fovSize / 2)),
                int(y + (h / 1) + (screenHeight / 2 - fovSize / 2)))

print('Program Closed')
